#include<iostream>
#include<queue>
#include<pthread.h>
#include<unistd.h>
#include<random>
using namespace std;

struct SimpleTask
{
	int value;
};

struct TestArg
{
	queue<SimpleTask*>* q;
	int testCnt;
	int maxQueueSize;
};

pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;


void* pthreadTaskSend(void* arg)
{
	TestArg *testArg = (TestArg*)arg;
	queue<SimpleTask*>* taskQueue = testArg->q;
	int testCnt = testArg->testCnt;
	int maxQueueSize = testArg->maxQueueSize;

	int i = 0;
	while (i < testCnt)
	{
		pthread_mutex_lock(&mutex);

		while (static_cast<int>(taskQueue->size()) == maxQueueSize)
		{
			std::cout<< "pthreadTaskSend wait\n";
			pthread_cond_wait(&cond,&mutex);
		}

		taskQueue->emplace(new SimpleTask{i});

		if (taskQueue->size() == 1)
		{
			std::cout<< "pthreadTaskSend signal\n";
			pthread_cond_signal(&cond);
		}
		pthread_mutex_unlock(&mutex);

		i++;
		usleep(rand() % 1000);
	}

	return NULL;
}

void* pthreadTaskRecv(void* arg)
{
	TestArg *testArg = (TestArg*)arg;
	queue<SimpleTask*>* taskQueue = testArg->q;
	int testCnt = testArg->testCnt;
	int maxQueueSize = testArg->maxQueueSize;

	int i = 0;
	while (i < testCnt)
	{
		pthread_mutex_lock(&mutex);

		while (static_cast<int>(taskQueue->size()) == 0)
		{
			std::cout<< "pthreadTaskRecv wait\n";
			pthread_cond_wait(&cond,&mutex);
		}

		SimpleTask* pTask = taskQueue->front();
		taskQueue->pop();

		if (static_cast<int>(taskQueue->size()) == maxQueueSize - 1)
		{
			std::cout<< "pthreadTaskRecv signal\n";
			pthread_cond_signal(&cond);
		}
		pthread_mutex_unlock(&mutex);

		cout<< "Recv Task. value = " << pTask->value << "\n";
		delete pTask;
		i++;
		usleep(rand() % 1000);
	}

	return NULL;
}


int main(void)
{
	pthread_t tTaskSend;
	pthread_t tTaskRecv;

	queue<SimpleTask*> qTask;
	int testCnt = 10000;

	TestArg arg;
	arg.q = &qTask;
	arg.testCnt = testCnt;
	arg.maxQueueSize = 32;

	pthread_create(&tTaskSend, NULL, pthreadTaskSend, (void*)&arg);
	pthread_create(&tTaskRecv, NULL, pthreadTaskRecv, (void*)&arg);
	pthread_join(tTaskSend, NULL);
	pthread_join(tTaskRecv, NULL);

	return 0;
}
